#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

use std::{
    collections::HashMap,
    ffi::OsStr,
    io::ErrorKind,
    path::{Path, PathBuf},
    sync::Arc,
    time::Duration,
};

use async_recursion::async_recursion;
use futures::future::join_all;
use tauri::{async_runtime::TokioJoinHandle, generate_context, State, Window};
use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::sync::Mutex;
use tokio::{
    fs,
    time::{sleep_until, Instant},
};

const CHANNEL_BUFFER_SIZE: usize = 32;

#[tokio::main]
async fn main() {
    tauri::Builder::default()
        .manage(TaskState {
            list: Arc::new(Mutex::new(Vec::new())),
            del: Arc::new(Mutex::new(Option::None)),
        })
        .invoke_handler(tauri::generate_handler![
            file_list,
            stop,
            delete_file,
            delete_file_list,
            delete_all
        ])
        .run(generate_context!())
        .expect("error while running tauri application");
}

struct TaskState {
    list: Arc<Mutex<Vec<TokioJoinHandle<()>>>>,
    del: Arc<Mutex<Option<TokioJoinHandle<()>>>>,
}

#[tauri::command]
async fn file_list(
    dir: Vec<String>,
    window: Window,
    state: State<'_, TaskState>,
) -> Result<String, String> {
    println!("state2filePath: {:?}", dir);
    match check_dir(&dir) {
        Ok(_) => {}
        Err(e) => {
            return Err(e);
        }
    };
    let list = state.list.clone();
    tokio::spawn(async move {
        scan_file(list, dir, window.clone()).await;
    });
    Ok("ok".into())
}

#[tauri::command]
async fn delete_all(del_file: Vec<String>, all_flag: bool, window: Window) -> Result<String, String> {
    println!("del_file: {:?}", del_file);
    tokio::spawn(async move {
        for ele in del_file {
            // sleep_until(Instant::now() + Duration::from_secs(1)).await;
            let s = ele.as_str();
            let _ = fs::remove_file(&ele).await;
            emit_del_file_item(s, &window);
        }
        emit_del_file_result(all_flag, "ok", &window);
    });
    Ok("ok".into())
}

fn check_dir(dir: &Vec<String>) -> Result<String, String> {
    for d in dir {
        let path = Path::new(d);
        if !path.exists() {
            return Err(format!("{} 目录不存在", d));
        }
        if path.is_file() {
            return Err(format!("{} 该路径不是一个目录", d));
        }
    }
    Ok("ok".into())
}

async fn scan_file(
    task_list: Arc<Mutex<Vec<TokioJoinHandle<()>>>>,
    dir_str: Vec<String>,
    window: Window,
) {
    let w2 = window.clone();
    let (tx, mut rx) = mpsc::channel(CHANNEL_BUFFER_SIZE);

    let mut lock = task_list.lock().await;
    let l = &mut *lock;
    for ele in dir_str {
        let tx_clone = tx.clone();
        let w_clone = window.clone();
        let handler = tokio::spawn(async move {
            // 是个异步递归函数
            read_dir0(tx_clone, Path::new(&ele).to_path_buf(), &w_clone).await;
            println!("{} 遍历完成", ele);
        });
        l.push(handler);
    }
    drop(lock);
    drop(tx);
    let result = collect_file(&mut rx).await;
    emit_file_list_result(result, &w2);
    println!("汇总结束");
}

#[tauri::command]
async fn delete_file(filepath: &str) -> Result<String, String> {
    println!("delete_file: {}", filepath);
    let result = fs::remove_file(filepath).await;
    match result {
        Ok(_) => {
            println!("删除成功");
            Ok("ok".into())
        }
        Err(error) => match error.kind() {
            ErrorKind::NotFound => Err("文件不存在，删除失败".into()),
            ErrorKind::PermissionDenied => Err("没有权限删除".into()),
            other_error => {
                println!("删除失败: {:?}", other_error);
                Err("删除失败".into())
            }
        },
    }
}

#[tauri::command]
async fn delete_file_list(filepath: Vec<&str>) -> Result<String, String> {
    println!("filePath: {:?}", filepath);

    for ele in filepath {
        let result = fs::remove_file(ele).await;
        match result {
            Ok(_) => {
                println!("删除成功");
            }
            Err(error) => match error.kind() {
                ErrorKind::NotFound => {
                    println!("文件不存在，删除失败");
                }
                ErrorKind::PermissionDenied => {
                    println!("没有权限删除");
                }
                other_error => {
                    println!("删除失败: {:?}", other_error);
                }
            },
        };
    }

    Ok("ok".into())
}

#[tauri::command]
async fn stop(state: State<'_, TaskState>) -> Result<String, String> {
    println!("收到stop!!!");

    let mut lock = state.list.lock().await;
    println!("len: {}", lock.len());

    for item in lock.iter_mut() {
        item.abort();
        println!("item abort");
    }

    Ok(String::from("ok"))
}

async fn collect_file(rx: &mut Receiver<FilePayload>) -> Vec<ResultPayload> {
    let mut map: HashMap<String, Vec<FilePayload>> = HashMap::new();
    while let Some(message) = rx.recv().await {
        // println!("GOT = {:?}", message);
        match map.get_mut(&message.md5) {
            Some(file_list) => {
                file_list.push(FilePayload { ..message });
            }
            None => {
                map.insert(message.md5.clone(), vec![message]);
            }
        }
    }
    let mut result_vec: Vec<ResultPayload> = vec![];
    for (key, value) in map {
        if value.len() > 1 {
            let md5 = key;
            let files: Vec<FileResult> = join_all(
                value
                    .iter()
                    .map(|i| async { FileResult::payload_to_result(i.to_owned()).await }),
            )
            .await;
            result_vec.push(ResultPayload { md5, files })
        }
    }
    result_vec.sort_by(|a, b| b.file_size().partial_cmp(&a.file_size()).unwrap());
    result_vec.truncate(100);
    result_vec
}

#[derive(Debug, Clone, serde::Serialize)]
struct FilePayload {
    path: PathBuf,
    md5: String,
}

#[derive(Debug, Clone, serde::Serialize)]
struct ResultPayload {
    md5: String,
    files: Vec<FileResult>,
}

impl ResultPayload {
    fn file_size(&self) -> u64 {
        return self.files[0].size;
    }
}

#[derive(Debug, Clone, serde::Serialize)]
struct FileResult {
    file_name: String,
    size: u64,
    file_path: String,
    extension: String,
}

impl FileResult {
    async fn payload_to_result(p: FilePayload) -> FileResult {
        let path = p.path;
        let file_path = path.display().to_string();
        let extension = path
            .extension()
            .unwrap_or(OsStr::new(""))
            .to_str()
            .unwrap()
            .to_owned();
        let file_name = path
            .file_name()
            .unwrap_or(OsStr::new(""))
            .to_str()
            .unwrap()
            .to_owned();
        let size = match fs::metadata(path).await {
            Ok(meta) => meta.len(),
            Err(e) => {
                println!("FilePayloadErr:{},Err:{:?}", file_path, e);
                0
            }
        };
        FileResult {
            file_name: file_name,
            size: size,
            file_path: file_path,
            extension: extension,
        }
    }
}

#[derive(Debug, Clone, serde::Serialize)]
struct FileItemPayload {
    path: String,
}

#[derive(Debug, Clone, serde::Serialize)]
struct DelItemPayload {
    all_flag: bool,
}

fn emit_file_item(file_path: &str, window: &Window) {
    window
        .emit(
            "file-item",
            FileItemPayload {
                path: String::from(file_path),
            },
        )
        .unwrap();
}

fn emit_del_file_item(file_path: &str, window: &Window) {
    window
        .emit(
            "del-file-item",
            FileItemPayload {
                path: String::from(file_path),
            },
        )
        .unwrap();
}

fn emit_del_file_result(all_flag: bool, str: &str, window: &Window) {
    window
        .emit(
            "del-file-result",
            DelItemPayload {
                all_flag : all_flag,
            },
        )
        .unwrap();
}

fn emit_file_list_result(result: Vec<ResultPayload>, window: &Window) {
    window.emit("file-list-result", result).unwrap();
    println!("emit emit_file_list_result");
}



#[async_recursion]
async fn read_dir0(tx: Sender<FilePayload>, path: PathBuf, window: &Window) {
    let v = path.to_str().unwrap();
    match fs::metadata(v).await {
        Ok(mt) => {
            mt.permissions().set_readonly(true);
        }
        Err(_) => {
            return ();
        }
    }

    let mut read_dir = fs::read_dir(path).await.unwrap();

    while let Some(entry) = read_dir.next_entry().await.unwrap() {
        //println!("{:?}", entry.path());
        //println!("fileName: {:?}", entry.file_name().as_os_str());
        let file_type = entry.file_type().await.unwrap();
        if file_type.is_dir() {
            //println!("是一个文件夹📁");
            //println!();
            read_dir0(tx.clone(), entry.path(), &window.clone()).await;
        } else {
            //println!("{:?} 是一个文件📃", entry.path());
            let file_data_result = fs::read(entry.path()).await;
            match file_data_result {
                Ok(file_data) => {
                    let digest = md5::compute(file_data);
                    let file_md5 = format!("{:x}", digest);
                    //println!("md5: {}", file_md5);
                    emit_file_item(entry.path().as_path().to_str().unwrap(), &window);
                    //println!("emit_file_item");
                    //println!("sendAwait");
                    match tx
                        .send(FilePayload {
                            md5: file_md5,
                            path: entry.path(),
                        })
                        .await
                    {
                        Ok(_) => {}
                        Err(_) => {}
                    }
                }
                Err(e) => {
                    println!("读取失败：{:?}", e);
                }
            }
        }
    }
}
